<!---
Copyright (c) 2007 Brian Billman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--->
<cfcomponent displayname="CFSmugUsers" extends="CFSmugBase" output="false">

	<cffunction name="getTree" access="public" output="false" returntype="array" hint="This method returns a complete tree, starting with Categories and descending into SubCategories (if any) and Albums (if any) for the specified user (identified by SessionID or (optional) NickName).">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="NickName" type="string" required="false" hint="Required if an anonymous SessionId" />
		<cfargument name="Heavy" type="boolean" required="false" default="false" hint="Different results returned based on SessionID" />
		<cfargument name="SitePassword" type="string" required="false" />
		<cfargument name="AlbumsOnly" type="boolean" required="false" default="false" />
		<cfargument name="ExcludeAlbums" type="boolean" required="false" default="false" />

		<cfset var response = 0 />
		<cfset var result = 0 />
		<cfset var argsCopy = structNew() />
		<cfset var c1 = 0 />
		<cfset var c2 = 0 />
		<cfset var passwordExists = false />

		<cfif StructKeyExists( Arguments, "Heavy" ) Or
			  StructKeyExists( Arguments, "SitePassword" )>
			<cfset passwordExists = true />
		</cfif>

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.getTree ), argsPassed = Arguments ) />

		<cfset response = doRemoteInvocation( method = "smugmug.users.getTree",
											args = argsCopy,
											secureMode = passwordExists ) />

		<cfset result = convertResponse( response ) />

		<cfif Arguments.AlbumsOnly And Not Arguments.ExcludeAlbums And ArrayLen( result )>

			<cfloop from="#ArrayLen( result )#" to="1" step="-1" index="c1">
				<!--- if subcategories exist --->
				<cfif StructKeyExists( result[c1], "SubCategories" )>
					<!--- loop over all subcategories, if subcategory contains no albums, delete it --->
					<cfloop from="#ArrayLen( result[c1].SubCategories )#" to="1" step="-1" index="c2">
						<cfif Not StructKeyExists( result[c1].SubCategories[c2], "Albums" )>
							<cfset ArrayDeleteAt( result[c1].SubCategories, c2 ) />
						</cfif>
					</cfloop>

					<!--- if subcategories array is empty, delete struct
							(i.e. it didn't contain any albums) --->
					<cfif Not ArrayLen( result[c1].SubCategories )>
						<cfset StructDelete( result[c1], "SubCategories") />
					</cfif>
				</cfif>

				<!--- if categoy does not contain any subcategories or albums, delete it --->
				<cfif Not StructKeyExists( result[c1], "Albums" ) And
					  Not StructKeyExists( result[c1], "SubCategories" )>
					<cfset ArrayDeleteAt( result, c1 ) />
				</cfif>
			</cfloop>

		<cfelseif Not Arguments.AlbumsOnly And Arguments.ExcludeAlbums And ArrayLen( result )>

			<cfloop from="#ArrayLen( result )#" to="1" step="-1" index="c1">
				<!--- if subcategories exist --->
				<cfif StructKeyExists( result[c1], "SubCategories" )>
					<!--- loop over all subcategories, if subcategory contains no albums, delete it --->
					<cfloop from="#ArrayLen( result[c1].SubCategories )#" to="1" step="-1" index="c2">
						<cfif StructKeyExists( result[c1].SubCategories[c2], "Albums" )>
							<cfset StructDelete( result[c1].SubCategories[c2], "Albums" ) />
						</cfif>
					</cfloop>
				</cfif>

				<!--- if categoy does not contain any subcategories or albums, delete it --->
				<cfif StructKeyExists( result[c1], "Albums" )>
					<cfset StructDelete( result[c1], "Albums" ) />
				</cfif>
			</cfloop>

		</cfif>

		<cfreturn result />
	</cffunction>

	<cffunction name="getTransferStats" access="public" output="false" returntype="struct" hint="Gets transfer statitistics for the logged-in user during the given Month and Year. SmugMug often only keeps the last few months (current + 2 previous), but you never know. A float is provided for Originals because videos often are only partially viewed.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="Month" type="numeric" required="true" />
		<cfargument name="Year" type="numeric" required="true" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.getTransferStats ), argsPassed = Arguments ) />

		<cfset response = doRemoteInvocation( method = "smugmug.users.getTransferStats",
											  args = argsCopy ) />


		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="convertResponse" access="private" output="false" returntype="any">
		<cfargument name="responseXML" type="xml" required="true" />
		<cfset var x = 0 />
		<cfset var s = 0 />

		<cftry>

			<cfif validateRESTResponse( Arguments.responseXML )>
				<cfset x = Arguments.responseXML.rsp />
			</cfif>


			<cfswitch expression="#LCase( x.method.XmlText )#">

				<cfcase value="smugmug.users.gettree">
					<cfset s = convertGetTreeResponse( x )>
				</cfcase>

				<cfcase value="smugmug.users.gettransferstats">
					<!---
						Array of structs: (Albums)
							int AlbumID
							int Bytes
							int Tiny
							int Thumb
							int Small
							int Medium
							int Large
							float Original
								Array of structs: (Images)
									int ImageID
									int Bytes
									int Tiny
									int Thumb
									int Small
									int Medium
									int Large
									float Original
					--->

					<!--- create struct for album stats --->
					<cfif StructKeyExists( x, "Album" )>
						<cfset s = createStatsStruct( StatsNode = x.Album, ForAlbum = true ) />

						<cfif StructKeyExists( x.Album, "Images" )>
							<cfset s["Images"] = ArrayNew( 1 ) />
							<cfif ArrayLen( x.Album.Images.Image )>
								<!--- create struct for stats for each image --->
								<cfloop from="1" to="#ArrayLen( x.Album.Images.Image )#" index="c1">
									<cfset s["Images"][c1] = createStatsStruct( StatsNode = x.Album.Images.Image[c1], ForAlbum = false ) />
								</cfloop>
							</cfif>
						</cfif>
					<cfelse>
						<cfset s = StructNew() />
					</cfif>
				</cfcase>


				<cfdefaultcase>
					<cfset logAction( file="CFSmug.CFSmugUsers.convertResponse", text="Message = Received response from unexpected method: '#x.method.XmlText#'", type="Warning" ) />
					<cfthrow type="CFSmug.CFSmugUsers.convertResponse" message="Received response from unexpected method: '#x.method.XmlText#'" />
				</cfdefaultcase>

			</cfswitch>

			<cfreturn s />


			<cfcatch type="CFSmug">
				<cfrethrow />
			</cfcatch>
			<cfcatch type="any">
				<cfset logAction( file="CFSmug.CFSmugUsers.convertResponse", text="Error converting response; Message=#cfcatch.message#; Detail=#cfcatch.detail#; Stacktrace=#cfcatch.stacktrace#", type="Error" ) />
				<cfthrow type="CFSmug.CFSmugUsers.convertResponse"
						message="Error converting response" detail="Message: #cfcatch.message#; Detail: #cfcatch.detail#" />
			</cfcatch>
		</cftry>
	</cffunction>

	<cffunction name="convertGetTreeResponse" access="private" output="false" returntype="array">
		<cfargument name="responseXMLNode" type="xml" required="true" />

		<cfset var x = Arguments.responseXMLNode />
		<cfset var s = ArrayNew( 1 ) />
		<cfset var c1 = 0 />
		<cfset var c2 = 0 />
		<cfset var c3 = 0 />
		<cfset var c4 = 0 />

		<!---
			Array of structs (Categories):
				int "CategoryID"
				String Title
					Array of structs: (SubCategories, if present)
						SubCategoryID
						Title
							Array of structs: (Albums, if present)
								struct: (Album Info, if Heavy is true)
					Array of structs: (Albums, if present, and no SubCat)
						struct: (Album Info, if Heavy is true)
		 --->

		<cfif StructKeyExists( x, "Categories" ) And
			  ArrayLen( x.Categories.Category )>

			<cfloop from="1" to="#ArrayLen( x.Categories.Category )#" index="c1">

				<cfset s[c1] = StructNew() />
				<cfset s[c1] = createCategoryStruct( CategoryNode = x.Categories.Category[c1] ) />

				<!--- take care of any albums in the top level category first... --->
				<cfif StructKeyExists( x.Categories.Category[c1], "Albums" ) And
					  ArrayLen( x.Categories.Category[c1].Albums.Album )>

					<cfset s[c1]["Albums"] = ArrayNew( 1 ) />
					<cfloop from="1" to="#ArrayLen( x.Categories.Category[c1].Albums.Album )#" index="c2">
						<cfset s[c1]["Albums"][c2] = createAlbumStruct( AlbumNode = x.Categories.Category[c1].Albums.Album[c2] ) />
					</cfloop>
				</cfif>

				<!--- then get any subcategories within the category and get the albums in the subcategories --->
				<cfif StructKeyExists( x.Categories.Category[c1], "SubCategories" ) And
					  ArrayLen( x.Categories.Category[c1].SubCategories.SubCategory )>

					<cfset s[c1]["SubCategories"] = ArrayNew( 1 ) />
					<cfloop from="1" to="#ArrayLen( x.Categories.Category[c1].SubCategories.SubCategory )#" index="c3">

						<cfset s[c1]["SubCategories"][c3] = StructNew() />
						<cfset s[c1]["SubCategories"][c3] = createSubCategoryStruct( SubCategoryNode = x.Categories.Category[c1].SubCategories.SubCategory[c3] ) />

						<!--- finally take care of any albums in the subcategory... --->
						<cfif StructKeyExists( x.Categories.Category[c1].SubCategories.SubCategory[c3], "Albums" ) And
							  ArrayLen( x.Categories.Category[c1].SubCategories.SubCategory[c3].Albums.Album )>

							<cfset s[c1]["SubCategories"][c3]["Albums"] = ArrayNew( 1 ) />
							<cfloop from="1" to="#ArrayLen( x.Categories.Category[c1].SubCategories.SubCategory[c3].Albums.Album )#" index="c4">
								<cfset s[c1]["SubCategories"][c3]["Albums"][c4] = createAlbumStruct( AlbumNode = x.Categories.Category[c1].SubCategories.SubCategory[c3].Albums.Album[c4] ) />
							</cfloop>
						</cfif>
					</cfloop>
				</cfif>
			</cfloop>
		</cfif>

		<cfreturn s />
	</cffunction>

</cfcomponent>